<?php

require_once 'UC_DBBase.php';
require_once '/CodeModel_Classes/PHPCodeProvider.php';
require_once '/PHPFileSystem_Classes/UC_PHPFileSystem.php';
require_once '/PHPFileSystem_Classes/UC_PHPTextStream.php';
require_once '/PHPFileSystem_Classes/UC_PHPIOMode.php';

abstract class UC_DBClass extends UC_DBBase
{
    public function __construct(UC_DBServer $Server)
    {
        parent::__construct($Server);
        
        $result_set = $this->get_Server_()->get_PDO()->query($this->select_statement);
        
        if($result_set)
        {
            if($result_set->rowCount() == 1)
            {
                $this->table_row = $result_set->fetch();
            }
            else
            {
                throw new Exception();
            }
        }
        else
        {
            throw new Exception();
        }
    }
        
    public function Associators_()
    {
        ;
    }
    
    public function Clone_()
    {
        ;
    }
    
    public function CompareTo_()
    {
        ;
    }
    
    public static final function CreateFromTable_(
        UC_DBServer $Server, $database_name, $table_name, $create_new = false)
    {
        
        /*
         * Make sure $database_name and $table_name are strings
         */
        
        if(!is_string($database_name))
        {
            throw new InvalidArgumentException(
                'The database name should be a string.');
        }
        
        if(!is_string($table_name))
        {
            throw new InvalidArgumentException(
                'The table name should be a string.');
        }
        
        /*
         * Check if the database exists
         */
        
        $db_path = new UC_DBObjectPath();
        $db_path->set_Server($Server);
        $db_path->set_is_Database(true);
        $db_path->set_Name($database_name);

        if(!UC_Database::Exists_($db_path))
        {
            throw new InvalidArgumentException(
                "The database does not exist : $database_name");
        }

        /*
         * Check if the table exists and if it does
         * get its UC_Table object
         */
        
        $table_path = new UC_DBObjectPath();
        $table_path->set_Server($Server);
        $table_path->set_Schema($database_name);
        $table_path->set_is_Table(true);
        $table_path->set_Table($table_name);
        $table_path->set_Name($table_name);

        if(UC_Table::Exists_($table_path))
        {
            $table = UC_Table::Get_($table_path);
        }
        else
        {
            throw new InvalidArgumentException(
                "Unable to locate table: $table_name");
        }

        /*
         * Get the table primary keys
         */
        
        $primary_keys = array();
        
        foreach($table->get_Columns() as $column)
        {
            if($column->get_InPrimaryKey())
            {
                $primary_keys[] = $column->get_Name();
            }
        }
        
        if(count($primary_keys) == 0)
        {
            throw new UC_DBException(
                "The table $table_name needs to have at least one primary key.");
        }
        
        /*
         * Create the UC_DBClass child class
         */
        
        $table_class = new TypeDeclaration(
            'UC_DBClass_' . $database_name . '_' . $table_name);
        $table_class->BaseTypes->add(new TypeReference('UC_DBClass'));

        /*
         * Add getter and setter methods to the class.
         */
        
        foreach($table->get_Columns() as $column)
        {
            /*
             * For each table column add a private field to the class
             */
            
            $field = new Field($column->get_Name());
            $field->Attributes->set_private(true);
            $table_class->Members->add($field);

            /*
             * Add getter method for each column
             */
            
            $getter = new Method('get_' . $field->get_name());
            $getter->Statements->add(new ExpressionStatement(
                new SnippetExpression('return $this->' . $column->get_Name())));
            $table_class->Members->add($getter);

            /*
             * Add setter method only if column is not in primary key
             */
            
            if(!$column->get_InPrimaryKey())
            {
                $setter = new Method('set_' . $field->get_name());
                $setter->Parameters->add(
                    new ParameterDeclarationExpression($column->get_Name()));
                $setter->Statements->add(new AssignStatement(
                    new FieldReferenceExpression(
                        $column->get_Name(), new ThisReferenceExpression()), 
                        new VariableReferenceExpression($column->get_Name())));

                /*
                 * If the setter is used, the table row values may have changed
                 */
                
                $set_touched = new MethodReferenceExpression(
                    'set_Touched_', new ThisReferenceExpression());
                $set_touched_invoke = new MethodInvokeExpression($set_touched);
                $set_touched_invoke->Parameters->add(new PrimitiveExpression(true));

                $setter->Statements->add(new ExpressionStatement($set_touched_invoke));

                $table_class->Members->add($setter);
            }

        }

        /*
         * The constructor
         * Add parameter for the server.
         */

        $constructor = new Method('__construct');
        $constructor->Parameters->add(new ParameterDeclarationExpression('Server'));
        $constructor->Parameters[0]->set_TypeHint(new TypeReference('UC_DBServer'));

        /*
         * Add parameter to the constructor for each primary key
         */
        
        foreach($primary_keys as $primary_key)
        {
            $constructor->Parameters->add(
                new ParameterDeclarationExpression($primary_key));
        }

        /*
         * Construct the Select statement to get the row from the table
         */
        
        $query_string = "Select * From $database_name.$table_name Where ";

        $first_key = true;

        for($i = 0; $i < count($primary_keys); $i++)
        {
            if($first_key)
            {
                $first_key = false;
                $query_string .= $primary_keys[$i] . " = '" . "\$" . 
                    $constructor->Parameters[$i + 1]->get_Name() . "'";
            }
            else
            {
                $query_string .= " And " . $primary_keys[$i] . " = '" . "\$" . 
                    $constructor->Parameters[$i + 1]->get_Name() . "'";
            }
        }

        /*
         * Add the Select statement to the constructor
         */
        
        $select_statement = new AssignStatement(
            new FieldReferenceExpression('select_statement', new ThisReferenceExpression()), 
            new DoubleQuotedStringExpression($query_string));

        $constructor->Statements->add($select_statement);

        /*
         * Invoke the parent constructor
         */
        
        $invoke_parent_statement = new ExpressionStatement(
            new MethodInvokeExpression(
                new MethodReferenceExpression(
                    '__construct', new ParentReferenceExpression())));
        
        $invoke_parent_statement->Expression->Parameters->add(
            new VariableReferenceExpression('Server'));
        
        $constructor->Statements->add($invoke_parent_statement);        

        /*
         * Assign row values to corresponding properties
         */
        
        foreach($table->get_Columns() as $column)
        {
            $indexer_expr = new ArrayIndexerExpression();
            $indexer_expr->TargetObject = 
                new FieldReferenceExpression('table_row', new ThisReferenceExpression());
            $indexer_expr->Indices->add(new PrimitiveExpression($column->get_Name()));
            $assign_statement = new AssignStatement(
                new FieldReferenceExpression($column->get_Name(), new ThisReferenceExpression()), 
                    $indexer_expr);
            
            $constructor->Statements->add($assign_statement);
        }

        $table_class->Members->add($constructor);   
        
        /*
         * The Put_() method updates the table row
         */
        
        $put_method = new Method('Put_');
        
        /*
         * Construct the Update statement
         */

        $update_query = "UPDATE $database_name.$table_name ";
        $first_column = true;

        foreach($table->get_Columns() as $column)
        {
            if(!$column->get_InPrimaryKey())
            {
                if($first_column)
                {
                    $first_column = false;
                    $update_query .= "SET " . $column->get_Name() . " = '" 
                        . "{\$this->get_" . $column->get_Name() . "()}'";
                }
                else
                {
                    $update_query .= ", " . $column->get_Name() . " = '" 
                        . "{\$this->get_" . $column->get_Name() . "()}'";
                }
            }
        }

        $update_query .= " WHERE ";
        $first_column = true;

        foreach($primary_keys as $primary_key)
        {
            if($first_column)
            {
                $first_column = false;
                $update_query .= $primary_key . 
                    " = '{\$this->get_" . $primary_key . "()}' ";
            }
            else
            {
                $update_query .= " AND " . $primary_key . 
                    " = '{\$this->get_" . $primary_key . "()}' ";
            }
        }

        $put_method->Statements->add(
            new AssignStatement(
                new FieldReferenceExpression('update_statement', new ThisReferenceExpression()), 
                new DoubleQuotedStringExpression($update_query)));

        $put_method->Statements->add(
            new ExpressionStatement(
                new MethodInvokeExpression(
                    new MethodReferenceExpression('Put_', new ParentReferenceExpression()))));
        
        $table_class->Members->add($put_method);

        /*
         * The Delete_() method
         */
        
        $delete_method = new Method('Delete_');
        
        $delete_query = "DELETE FROM $database_name.$table_name WHERE ";
        
        $first_column = true;
        foreach($primary_keys as $primary_key)
        {
            if($first_column)
            {
                $first_column = false;
                $delete_query .= $primary_key . 
                    " = '{\$this->get_" . $primary_key . "()}' ";
            }
            else
            {
                $delete_query .= " AND " . $primary_key . 
                    " = '{\$this->get_" . $primary_key . "()}' ";
            }
        }
        
        $delete_method->Statements->add(
            new AssignStatement(
                new FieldReferenceExpression('delete_statement', new ThisReferenceExpression()), 
                new DoubleQuotedStringExpression($delete_query)));
        
        $delete_method->Statements->add(
            new ExpressionStatement(new MethodInvokeExpression(
                new MethodReferenceExpression('Delete_', new ParentReferenceExpression()))));
        
        $table_class->Members->add($delete_method);
        
        $options = new CodeGeneratorOptions();
        $options->set_VerbatimOrder(false);
        $writer = new IndentedTextWriter();
        $provider = new PHPCodeProvider();

        $provider->generateCodeFromType($table_class, $writer, $options);
        echo '<pre>' . str_replace('<br />', '', highlight_string($writer->get_String(), true)) . '<pre/>';
        $file = UC_PHPFileSystem::create_text_file(dirname(__FILE__) . 
            '/Database_Classes/UC_DBClass_' . 
            $database_name . '_' . $table_name . '.php', true);

        $file->write_line('<?php');
        $file->write_line($writer->get_String());
        $file->write_line('?>');
        $file->close();

    }
    
    public function Delete_()
    {
        $this->get_Server_()->get_PDO()->exec($this->delete_statement);
    }
    
    public function ExecuteMethod_()
    {
        ;
    }
    
    static function Exists_(UC_DBObjectPath $object_path)
    {
        ;
    }

    static function Get_(UC_DBObjectPath $object_path)
    {
        ;
    }
    
    static function Instances_()
    {
        ;
    }
    
    public function Put_()
    {
        if($this->get_Touched_())
        {
            $this->get_Server_()->get_PDO()->exec($this->update_statement);
        }
     }
    
    public function References_()
    {
        ;
    }
    
    static function SpawnInstance_()
    {
        ;
    }
    
    protected $database_name;
    protected $table_name;
    protected $table_row;
    protected $primary_keys = array();
    protected $select_statement;
    protected $update_statement;
    protected $delete_statement;
    protected $where_clause;
}

?>
